package com.lin.fengxiu.core.interceptors;

import com.auth0.jwt.interfaces.Claim;
import com.lin.fengxiu.core.LocalUser;
import com.lin.fengxiu.exception.http.ForbiddenException;
import com.lin.fengxiu.exception.http.UnAuthenticatedException;
import com.lin.fengxiu.model.User;
import com.lin.fengxiu.service.UserService;
import com.lin.fengxiu.util.JwtToken;
import org.apache.commons.lang3.StringUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.lang.Nullable;
import org.springframework.web.method.HandlerMethod;
import org.springframework.web.servlet.ModelAndView;
import org.springframework.web.servlet.handler.HandlerInterceptorAdapter;

import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.util.Map;
import java.util.Optional;

/**
 * @ClassName PermmissionInterceptor
 * @Description TODO 继承HandlerInterceptorAdapter类实现拦截器interceptor
 * @Author wushaopei
 * @Date 2021/3/22 6:58
 * @Version 1.0
 */
public class PermmissionInterceptor extends HandlerInterceptorAdapter {

    @Autowired
    private UserService userService;

    public PermmissionInterceptor() {
        super();
    }

    /**
     * @Description TODO 前置处理
     * @param request
     * @param response
     * @param handler
     * @return
     * @throws Exception
     */
    @Override
    public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
        // 获取scopeLevel
        Optional<ScopeLevel> scopeLevel = this.getScopeLevel(handler);
        if(!scopeLevel.isPresent()){
            return true;
        }
        // 获取令牌
        String bearerToken = request.getHeader("Authorization");
        // 令牌判空，若为空直接抛出异常；不进行 scopeLevel 的check
        if(StringUtils.isEmpty(bearerToken)){
            throw new UnAuthenticatedException(10004);
        }
        // 查看 token 开头是否为 Bearer ，若为Bearer，说明是所需要的。解析获取token
        if(!bearerToken.startsWith("Bearer")){
            throw new UnAuthenticatedException(10004);
        }
        String tokens[] = bearerToken.split(" ");
        if(!(tokens.length == 2)){
            throw new UnAuthenticatedException(10004);
        }
        // 拆分获取 token
        String token = tokens[1];
        // 使用JwtToken的getClaims 校验令牌是否符合规则
        Optional<Map<String, Claim>> optional = JwtToken.getClaims(token);
        Map<String,Claim> map = optional.orElseThrow(
                ()-> new UnAuthenticatedException(10004));
        // 校验访问权限
        boolean valid = this.hasPermission(scopeLevel.get(),map);
        // 调用LocalUser写入全局变量
        if(valid){
            this.setToThreadLocal(map);
        }
        return valid;
    }

    /**
     * @Description TODO 全局共享的静态变量
     * @param map
     */
    private void setToThreadLocal(Map<String,Claim> map){
        Long uid = map.get("uid").asLong();
        Integer scope = map.get("scope").asInt();
        User user = userService.getUserById(uid);
        LocalUser.set(user,scope);
    }


    /**
     * @Description TODO 校验用户权限是否满足
     * @param scopeLevel
     * @param map
     * @return
     */
    private boolean hasPermission(ScopeLevel scopeLevel,Map<String,Claim> map){
        Integer level = scopeLevel.value();
        Integer scope = map.get("scope").asInt();
        if(level > scope){
            throw new ForbiddenException(10005);
        }
        return true;
    }


    @Override
    public void postHandle(HttpServletRequest request, HttpServletResponse response, Object handler, @Nullable ModelAndView modelAndView) throws Exception {
         super.postHandle(request,response,handler,modelAndView);
    }

    /**
     * @Description TODO 拦截器后置处理
     * @param request
     * @param response
     * @param handler
     * @param ex
     * @throws Exception
     */
    @Override
    public void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, @Nullable Exception ex) throws Exception {
           LocalUser.clear();
           super.afterCompletion(request,response,handler,ex);
    }

    /**
     * @Description TODO 获取当前接口的scopeLevel
     * @param handler
     * @return
     */
    public Optional<ScopeLevel> getScopeLevel(Object handler){
        if( handler instanceof HandlerMethod){
            HandlerMethod handlerMethod = ((HandlerMethod) handler);
            ScopeLevel scopeLevel = handlerMethod.getMethod().getAnnotation(ScopeLevel.class);
            if(scopeLevel == null){
                return Optional.empty();
            }
            return Optional.of(scopeLevel);
        }
        return Optional.empty();
    }
}
